package org.charles.zhifou.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhipu.oapi.service.v4.model.ModelData;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.charles.zhifou.common.exception.BusinessException;
import org.charles.zhifou.common.exception.ThrowUtils;
import org.charles.zhifou.common.response.ResponseCodeEnum;
import org.charles.zhifou.constant.ZhiPuAiConstant;
import org.charles.zhifou.enums.ReviewStatusEnum;
import org.charles.zhifou.enums.UserRoleEnum;
import org.charles.zhifou.manager.ZhiPuAiManager;
import org.charles.zhifou.mapper.QuestionMapper;
import org.charles.zhifou.model.dto.question.QuestionContentDTO;
import org.charles.zhifou.model.entity.App;
import org.charles.zhifou.model.entity.Question;
import org.charles.zhifou.model.entity.User;
import org.charles.zhifou.service.AppService;
import org.charles.zhifou.service.QuestionService;
import org.charles.zhifou.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @author charles
* @description 针对表【question(题目)】的数据库操作Service实现
* @createDate 2024-08-06 09:11:36
*/
@Service
@Slf4j
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question>
    implements QuestionService {

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;

    @Resource
    private ZhiPuAiManager aiManager;

    @Override
    public Long saveQuestion(Question question) {
        // 如果应用 id 不存在则抛异常
        App app = appService.getById(question.getAppId());
        ThrowUtils.throwIf(app == null, ResponseCodeEnum.PARAMS_ERROR, "应用不存在");

        // 写入数据库
        boolean isSuccessful = this.save(question);
        ThrowUtils.throwIf(!isSuccessful, ResponseCodeEnum.OPERATION_ERROR, "写入数据库失败");

        // 返回新建题目 id
        return question.getId();
    }

    @Override
    public Boolean removeQuestionById(Long id) {
        Question question = this.getById(id);

        // 如果应用 id 不存在则抛异常
        App app = appService.getById(question.getAppId());
        ThrowUtils.throwIf(app == null, ResponseCodeEnum.PARAMS_ERROR, "应用不存在");

        // 仅本人或管理员可删除
        User loginUser = userService.getLoginUser();
        if (!question.getUserId().equals(loginUser.getId()) && !UserRoleEnum.ADMIN.equals(loginUser.getUserRole())) {
            throw new BusinessException(ResponseCodeEnum.NO_AUTH_ERROR);
        }

        return this.removeById(id);
    }

    @Override
    public Boolean updateQuestion(Question question) {
        // 如果应用 id 不存在则抛异常
        App app = appService.getById(question.getId());
        ThrowUtils.throwIf(app == null, ResponseCodeEnum.PARAMS_ERROR, "应用不存在");

        return this.updateById(question);
    }

    @Override
    public Boolean editQuestion(Question question) {
        // 如果题目 id 不存在则抛异常
        Question questionToEdit = this.getById(question.getId());
        ThrowUtils.throwIf(questionToEdit == null, ResponseCodeEnum.PARAMS_ERROR, "题目不存在");

        // 仅本人或管理员可删除
        User loginUser = userService.getLoginUser();
        if (!questionToEdit.getUserId().equals(loginUser.getId()) && !UserRoleEnum.ADMIN.equals(loginUser.getUserRole())) {
            throw new BusinessException(ResponseCodeEnum.NO_AUTH_ERROR);
        }

        // 因为修改了题目, 将对应的应用审核字段设置为空, 状态设置为未审核
        appService.update(
                new LambdaUpdateWrapper<App>()
                        .set(App::getReviewStatus, ReviewStatusEnum.REVIEWING)
                        .set(App::getReviewMessage, null)
                        .set(App::getReviewerId, null)
                        .set(App::getReviewTime, null)
                        .eq(App::getId, questionToEdit.getAppId())
        );

        // 其实只会修改题目
        return this.updateById(question);
    }

    @Override
    public List<QuestionContentDTO> aiGenerateQuestionsSync(Long appId, String userMessage) {
        // AI 生成
        String result = aiManager.doSyncRequest(ZhiPuAiConstant.GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null);

        // 截取需要的 JSON 信息
        int start = result.indexOf("[");
        int end = result.lastIndexOf("]");
        String json = result.substring(start, end + 1);

        return JSONUtil.toList(json, QuestionContentDTO.class);
    }

    @Override
    public SseEmitter aiGenerateQuestionsAsync(Long appId, String userMessage) {
        // 建立 SSE 连接对象, 0 表示永不超时
        SseEmitter sseEmitter = new SseEmitter(0L);

        // AI 生成, SSE 流式返回
        Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(
                ZhiPuAiConstant.GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, 0.9f);

        // 左括号计数器, 除了默认值外, 当回归为 0 时, 表示左括号等于右括号, 可以截取
        AtomicInteger counter = new AtomicInteger(0);

        // 用于拼接完整题目
        StringBuilder stringBuilder = new StringBuilder();

        modelDataFlowable
                // 使用全局线程池处理 (后面可以考虑隔离线程池)
                .observeOn(Schedulers.io())
                // 获取数据 (字符串片段)
                .map(modelData -> modelData.getChoices().get(0).getDelta().getContent())
                // 过滤空字符
                .map(message -> message.replaceAll("\\s", ""))
                // 去除过滤空字符后, 变为空字符串的情况
                .filter(StrUtil::isNotBlank)
                // 将 String 转为 List<Character>, 便于后续处理
                .flatMap(message -> Flowable.fromIterable(
                        message.chars().mapToObj(c -> (char) c).toList()
                ))
                // 处理字符流, 截取有效的题目 JSON, 返回给前端
                .doOnNext(c -> {
                    // 如果是 '{', 计数器 + 1
                    if (c == '{') {
                        counter.addAndGet(1);
                    }
                    if (counter.get() > 0) {
                        stringBuilder.append(c);
                    }
                    if (c == '}') {
                        counter.addAndGet(-1);
                        if (counter.get() == 0) {
                            // 可以拼接题目, 并且通过 SSE 返回给前端
                            sseEmitter.send(JSONUtil.toJsonStr(stringBuilder.toString()));
                            // 重置, 准备拼接下一道题
                            stringBuilder.setLength(0);
                        }
                    }
                })
                .doOnError(e -> log.error("sse error", e))
                .doOnComplete(sseEmitter::complete)
                .subscribe();

        return sseEmitter;
    }
}




